﻿<!--
    Mango - Open Source M2M - http://mango.serotoninsoftware.com
    Copyright (C) 2006-2011 Serotonin Software Technologies Inc.
    @author Matthew Lohbihler
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/.
 -->

<h1>Обзор</h1>
<p>Метаточки конфигурируются при помощи создания «контекста» существующих точек, который становится доступным при выполнении сценария. Такой контекстной точкой может стать любая точка, сохраненная в Scada-LTS, в том числе и редактируемая в текущее время точка. (Чтобы текущая точка появилась в списке контекстных точек, она должна быть сохранен, т.е. она не должен быть вновь созданной).</p>

<h1>Конфигурация точки </h1>
<p>Значение параметра Тип данных определяет тип данных, которые будет возвращать сценарий. Точка будет пытаться преобразовать возвращаемое значение в этот тип. </p>
<p>Контекст сценария определяет объекты, которые будут доступны при выполнении сценария. Каждому добавляемому объекту должна быть назначена переменная Var, т.е. имя переменной, которой будет представлено значение в сценарии. Написание имен переменных должно соответствовать именам переменных ECMA-сценария: они должны начинаться либо с буквы, либо с нижнего прочерка, и не должны содержать пробелов. Кроме того, могут быть и другие ограничения. Если имена переменных в сценарии заданы некорректно, вы получите предупреждение об исключительной ошибке подтверждения или выполнения сценария. Чтобы добавить объект в контекст, выберите его из списка и нажмите пиктограмму <img src="images/add.png"/> Чтобы удалить существующий объект из контекста, нажмите пиктограмму <img src="images/bullet_delete.png"/>, связанную с объектом. Если объект не задействован в сценарии, его не нужно добавлять в контекст, так как это создает дополнительную нагрузку при подготовке данных для сценария. Кроме того, невостребованные переменные сценария могут вызвать неправильное исполнение сценария. (См. ниже раздел Исполнение сценария). Однако бывают и противоположные ситуации: иногда бывает удобно вставить переменную в контекст, чтобы запустить исполнения сценария</p>
<h1>Скрипты</h1>
<p>Область Сценариев – это область, в которой вводятся сценарии. Сценарием может быть любой правильный ECMA-сценарий, написанный внутри функции. Сценарий всегда должен возвращать значение. Простейший сценарий может выглядеть вот так:</p>
<pre>return x.value;</pre>
<p>... где х – это переменная сценария, определенная в контексте. Возвращаемое сценарием значение – это текущее значение объекта, которому назначена переменная х. С помощью сценариев можно задавать традиционные математические функции. Вот более сложный пример:</p>
<pre>return Math.sqrt(x.value * 3);</pre>
<p>Данный сценарий возвращает квадратный корень текущего значения объекта х, умноженного на 3. (Примечание: математический объект задан при помощи Java-сценария. Ознакомьтесь с более подробной информацией в документации на ECMA-сценарии). В более сложных сценариях используются локально заданные переменные, циклы и логические структуры. Например:</p>
<pre>var t = x.value + y.value;
if (b.value) {
    for (var i=0; i&lt;5; i++) {
        tmp += x.value - y.value;
    }
}
else {
    tmp = -tmp;
}
return tmp;</pre>
<p>Приведенный выше сценарий не предназначен для вычисления какого-либо нужного значения, он просто демонстрирует, насколько сложными могут быть сценарии. </p>
<p>Помимо контекста ECMA-скриптов в Scada-LTS также используется несколько полезных глобальных функций, в том числе max(), min(), avg(), and sum(). (Данные функции реализованы в файле сценария, расположенном в папке WEB-INF/scripts/scriptFunctions.js. Этот файл может быть изменен или дополнен так, как Вам нужно, для реализации Ваших собственных глобальных функций. (Файл загружается один раз при запуске Scada-LTS, поэтому, чтобы изменения вступили в силу, требуется перезапуск системы). Чтобы использовать глобальные переменные, просто вызовите их из сценария, например:</p>
<pre>return max(x.value, y.value, z.value);</pre>
<p>Данный сценарий возвращает максимальное из текущих значений переменных 'x', 'y', и 'z'. Глобальные функции могут оперировать любым количеством параметров.<br>
Как только сценарий введен, нажмите пиктограмму <img src="images/accept.png"/>, чтобы выполнить его и проверить результат.</p>
<h1>Значения времени</h1>
<p>Метка времени последнего значения также присутствует в сценарии. Можно использовать следующие поля:</p>
<dl>
  <dt>p.time</dt>
  <dd>возвращает метку времени значения в миллисекундах с начала эпохи </dd>
  <dt>p.millis</dt>
  <dd>0-999 участок параметра p.time в миллисекундах</dd>
  <dt>p.second</dt>
  <dd>0-60</dd>
  <dt>p.minute</dt>
  <dd>0-60</dd>
  <dt>p.hour</dt>
  <dd>0-23</dd>
  <dt>p.day</dt>
  <dd>1-28,31</dd>
  <dt>p.dayOfWeek</dt>
  <dd>1-7 - дни недели, где 1 – это воскресенье</dd>
  <dt>p.dayOfYear</dt>
  <dd>1-365,366 - день года </dd>
  <dt>p.month</dt>
  <dd>1-12 - мсяц </dd>
  <dt>p.year</dt>
  <dd>год, из четырех цифр</dd>
</dl>
<p>Чтобы явно присвоить метку времени значению, установите контекстную переменную TIMESTAMP перед выражением. Значение, которое присваивается этой переменной должно быть в миллисекундах с начала эпохи (не просто дата). Например: </p>
<pre>TIMESTAMP = new Date().getTime();
return p.value + 1;
</pre>

<h1>Контекстные точки </h1>
<p>Переменная сценария, которая представляет объект в сценарии, фактически называется «объект» в терминологии Java-сценариев. Объект является хранилищем значений и функций, которые идентифицируются по имени свойства. Чтобы получить описание свойств, применяемых в сценариях, воспользуйтесь свойством help (помощь), например:</p>
<pre>return x.help;</pre>
<p>Этот сценарий работает лучше всего, если задан алфавитно-числовой тип данных, но это не обязательное требование. Свойство help аналогично свойству toString() function, оно присутствует во всех контекстных объектах (т.е. не только в переменных объекта).</p>
<p>Свойство Значение является текущим значением объекта. Типы значений в Java-сценариях соответствуют типам данных Mango: двоичный соответствует булевому, числовой соответствует числу с плавающей точкой, тип с несколькими состояниями соответствует целому числу, а алфавитно-цифровой соответствует строке.</p>
<p>Кроме того, каждая переменная сценария реализует четыре функции. Значения, возвращаемые этими функциями, зависят от типа данных объекта, к которому относится переменная. Как уже говорилось выше, свойство help может использования для получения описания свойств объектов. Для параметра &quot;periodType&quot; (тип периода) во всех функциях могут использоваться следующие предопределенные глобальные переменные: SECOND (секунда), MINUTE (минута), HOUR (час), DAY (день), WEEK (неделя), MONTH (месяц), и YEAR (год).</p>
<p>Функция <strong>ago()</strong> возвращает значение, которое объект имел указанное количество времени назад. Например, запрос &quot;x.ago(HOUR, 3)&quot; возвращает значение, которое имел объект ровно 3 часа назад.</p>
<p>Функция <strong>past()</strong> возвращает значение объекта, содержащее статистику за период с указанного времени до текущего момента. Ниже приводится описание различных статистических объектов.</p>
<p>Функции <strong>prev() </strong>и <strong>previous()</strong> идентичны; просто вторая записывается полностью. Эти функции возвращают такую же статистическую информацию, как функция past(), но в течение другого временного диапазона. Время начала и конца периода разбивается таким образом, что оно соответствует типу периода. Например, если тип периода –HOURLY (каждый час), а количество периодов – 1, и функция запущена в 18:05, то будет использоваться временной диапазон с 17:00 (включительно) до 18:00 (исключая). Если количество периодов было бы, например, равно 3, то временной диапазон был бы с 15:00 до 18:00. Аналогично период MONTH (месяц) начинает временной диапазон в полночь первого дня предыдущего месяца и заканчивает его в последний день предыдущего месяца (если количество периодов равно 1). Другие типы периодов устанавливаются аналогично. Неделя начинается в понедельник в полночь, в соответствии со стандартами ISO.</p>

<h1>Статистические точки </h1>
<p>Функции past(), prev(), и previous() возвращают статистические элементы. (См. раздел Контекстные объекты). Свойства возвращаемого элемента зависят от типа данных точки. Значения времени в объектах хранятся в формате целых чисел, и исчисляются в миллисекундах, начиная с 00:00 1 января 1970 г.</p>
<p>
Элемент <strong>Аналоговая статистика</strong> <b>(AnalogStatistics</b>) возвращается числовыми объектами. Он содержит следующие свойства:</p>
<ul>
  <li><b>minimum</b>: (с плавающей точкой) минимальное значение объекта, достигнутое за период времени</li>
  <li><b>minimum time</b>: (целое) время, когда было зафиксировано минимальное значение </li>
  <li><b>maximum</b>: (с плавающей точкой) максимальное значение, достигнутое за период времени </li>
  <li><b>maximum time</b>: (целое) время, когда было зафиксировано максимальное значение</li>
  <li><b>average</b>: (с плавающей точкой) среднее значение объекта за период </li>
  <li><b>sum</b>: (с плавающей точкой) сумма всех обновленных значений за период (применяется для подсчета импульсов)</li>
  <li><b>count</b>: (целое) количество обновлений значения за период </li>
  <li><b>noData</b>: (булевая) были ли в течение периода какие-либо данные (true, если период предшествовал первому зафиксированному значению) </li>
  <li><b>realStart</b>: (целое) действительное время запуска, используемое для вычислений (в случае, если время старта предшествует первому зафиксированному значению) </li>
  <li><b>end</b>: (целое) время завершения, используемое для вычислений </li>
</ul>
<p>Например, следующий сценарий возвращает минимальное значение 'n' за прошедший час:</p>
<pre>return n.past(HOUR).minimum;</pre>

<p>
Двоичные объекты и объекты с несколькими состояниями возвращают объект Список запусков и исполнений (<b>StartsAndRuntimeList</b>). Он содержит следующие свойства: </p>
<ul>
  <li><b>realStart</b>: (целое) действительное время запуска, используемое для вычислений (в случае, если запуск предшествовал первому зафиксированному значению объекта) </li>
  <li><b>end</b>: (целое) время завершения, используемое для вычислений </li>
  <li><b>data</b>: (массив) список отдельных объектов Запуск и исполнение.</li>
</ul>
Каждый объект Запуск и исполнение имеет следующие свойства:
<ul>
  <li><b>value</b>: (булевая для двоичных данных, целое для данных с несколькими состояниями) состояние объекта, к которому применяются оставшиеся свойства </li>
  <li><b>starts</b>: (целое) количество изменений состояния в течение периода </li>
  <li><b>runtime</b>: (целое) количество времени в миллисекундах, в течение которого объект работал, в пределах заданного периода</li>
  <li><b>proportion</b>: (с плавающей точкой) отношение времени исполнения к общему периоду времени </li>
  <li><b>percentage</b>: (с плавающей точкой) <b>proportion</b> * 100</li>
</ul>

<p>
Чтобы получить доступ к конкретному объекту Запуск и исполнение (StartAndRuntime) в списке, используйте функцию get(). Например, следующий сценарий возвращает значение соотношения времени, когда 'b' был в состоянии 'false' в течение периода, равного 2 месяцам.</p>
<pre>return b.past(MONTH, 2).get(false).proportion;</pre>

<p>Объект Счетчик изменений значения (<b>ValueChangeCounter</b>) возвращается алфавитно-цифровыми точками. Он содержит единственное свойство – количество изменений, т.е. число изменений точки в течение заданного периода. Например, следующая запись возвращает количество изменений значения 'a' в течение предшествующих 45 минут. <br>
return b.previous(MINUTE, 45);</p>
<pre>return b.previous(MINUTE, 45);</pre>

<p>Если сценарием возвращается переменная, то берется ее текущее значение. Таким образом, приведенный ниже сценарий будет возвращать текущее значение 'x':</p>
<pre>return x;</pre>
<p>Однако, этот сценарий не вернет сумму 'x' и 'y':</p>
<pre>return x + y;</pre>
<p>... но этот сценарий вернет:</p>
<pre>return x.value + y.value;</pre>

<h1>Выполнение сценария</h1>
<p>Каждый раз при выполнении сценария объекта, результат приписывается объекту в качестве обновления значения. Количество запусков сценария можно задать при помощи значения Событие обновления. Установка значения «Обновление контекста» будет запускать сценарий каждый раз, когда объект в его контексте обновляется. Другая настройка заставит сценарий запускаться при указанном временном событии.</p>
<p>Установка Задержки выполнения может использоваться для предотвращения многократного исполнения сценария. Часто в контексте сценария используется несколько объектов, но при использовании функции «Обновление контекста» из-за того, что обновление каждого объекта приводит к выполнению сценария, обновление всех объектов, скажем, каждый час, заставит сценарий запускаться столько раз в течение часа, сколько объектов в нем содержится. Аналогично для запуска временных сценариев, сценарий может запуститься немного раньше, чем запустится обновление его контекстных объектов, что может привести к ложным результатам. Функцию задержки выполнения можно использовать для того, чтобы сценарии отрабатывали более корректно. При использовании функции «Обновления контекста», сценарий не будет запускаться до обновления контекста. Запуск сценария начнется через заданное количество секунд после последнего обновления. Для временных сценариев, сценарий будет запускаться через заданное количество времени в секундах после заданного временного события.</p>
<p>Налаштування <b>Context change</b> призводить до запуску сценарію щоразу, коли змінюється точка в його контексті. </p>
<h1>Дополнительные примеры</h1>
<p>Следующий сценарий вычисляет скользящее среднее значение объектов 'n1' и 'n2' за час:</p>
<pre>return avg(b1.past(HOUR).average, b2.past(HOUR).average);</pre>

<p>Данный сценарий вычисляет ежедневное количество импульсов, полученное со счетчика импульсов 'pulse' (при выполнении в «Начале дня»):</p>
<pre>return pulse.value - pulse.ago(DAY);</pre>

<p>Следующий сценарий не очень часто используется, но тем не мене интересен. Он циклически перемещается между цифрами 1, 2, и 3, но меняет случайным образом 1 на 100 запусков.</p>
<pre>var r = Math.random();
if (r &gt; 0.01)
    return x.value;

if (x.value == 3)
    return 1;
return x.value + 1;</pre>

<p>Следующий сценарий возвращает сумму целых значений двух числовых объектов 'r' и 't':</p>
<pre>return parseInt(t.value) + parseInt(r.value);</pre>